package cauc;

import Protocol.BasicProtocol;
import Protocol.DownlinkProtocol;
import Protocol.SocketUtil;
import Protocol.UplinkProtocol;

import javax.net.SocketFactory;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.Socket;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ConnectionThread implements Runnable{

    private static final int SUCCESS = 100;
    private static final int FAILED = -1;

    private Socket socket;
    private String IP;
    private String port;
    private SendTask sendTask;
    private ReceiveTask receiveTask;
    private MainForm mainForm;

    private boolean isSockAvailable;
    private boolean closeSendTask;

    protected volatile ConcurrentLinkedQueue<BasicProtocol> dataQueue = new ConcurrentLinkedQueue<>();

    public ConnectionThread(String ip, String port, MainForm mainForm){
        this.IP = ip;
        this.port = port;
        this.mainForm = mainForm;
    }

    @Override
    public void run() {
        try {
            try {
                socket = SocketFactory.getDefault().createSocket(IP, Integer.parseInt(port));
                mainForm.port.setEnabled(false);
                mainForm.connect.setEnabled(false);
                mainForm.closeCMU.setEnabled(true);
                mainForm.sendMessage.setEnabled(true);
                mainForm.preview.setEnabled(true);
                mainForm.stateLabel.setText("当前状态：已启动");
                //#008000 ->纯绿色RGB
                mainForm.stateLabel.setForeground(Color.decode("#008000"));
            } catch (IOException e) {
                JOptionPane.showMessageDialog(null, "服务器连接异常，请检查网络");
                return;
            }

            isSockAvailable = true;

            //开启接收线程
            receiveTask = new ReceiveTask();
            receiveTask.inputStream = socket.getInputStream();
            receiveTask.start();

            //开启发送线程
            sendTask = new SendTask();
            sendTask.outputStream = socket.getOutputStream();
            sendTask.start();

        }catch (IOException e){
            e.printStackTrace();
        }

    }

    /**
     * 向发送队列中添加报文并激活
     * @param protocol
     */
    public void addRequest(DownlinkProtocol protocol){
        dataQueue.add(protocol);
        toNotifyAll(dataQueue);
    }

    public synchronized void stop(){
        //关闭接收线程
        closeReceiveTask();

        closeSendTask = true;
        toNotifyAll(dataQueue);

        //关闭socket
        closeSocket();

        //清除数据
        clearData();

        JOptionPane.showMessageDialog(null, "断开连接");
    }

    public void closeReceiveTask(){
        if(receiveTask != null){
            receiveTask.interrupt();
            receiveTask.isCancle = true;
            if(receiveTask.inputStream != null){
                try{
                    if(isSockAvailable && !socket.isClosed() && socket.isConnected()){
                        socket.shutdownInput(); //解决SocketException的问题
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }
                SocketUtil.closeInputStream(receiveTask.inputStream);
                receiveTask.inputStream = null;
            }
        }
    }

    private void closeSendTask(){
        if(sendTask != null){
            sendTask.isCancle = true;
            sendTask.interrupt();
            if(sendTask.outputStream != null){
                synchronized (sendTask.outputStream){
                    SocketUtil.closeOutputStream(sendTask.outputStream);
                    sendTask.outputStream = null;
                }
            }

            sendTask = null;
        }
    }

    private void closeSocket(){
        if(socket != null){
            try{
                socket.close();
                isSockAvailable = false;
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    private void clearData(){
        dataQueue.clear();
    }

    private  void toWait(Object o){
        synchronized (o){
            try{
                o.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    protected  void toNotifyAll(Object o){
        synchronized (o){
            o.notifyAll();
        }
    }

    private boolean isConnected(){
        if(socket.isClosed() || !socket.isConnected()){
            ConnectionThread.this.stop();
            return false;
        }
        return true;
    }

    /**
     * 接受线程
     */
    public class ReceiveTask extends Thread{
        private boolean isCancle;
        private InputStream inputStream;

        @Override
        public void run() {
            while(!isCancle){
                if(!isConnected()){
                    isCancle = true;
                    break;
                }

                if(inputStream != null){
                    BasicProtocol receivedProtocol = SocketUtil.readFromStream(inputStream, UplinkProtocol.PROTOCOL_TYPE);
                    if(receivedProtocol != null){
                        mainForm.messageListModel.addElement(receivedProtocol);
                    }else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * 发送线程
     */
    public class SendTask extends Thread{
        private boolean isCancle = false;
        private OutputStream outputStream;

        @Override
        public void run() {
            while (!isCancle){
                if(!isConnected()){
                    break;
                }

                BasicProtocol protocol = dataQueue.poll();
                if(protocol == null){
                    toWait(dataQueue);
                    if(closeSendTask){
                        closeSendTask();
                    }
                }else if(outputStream != null){
                    synchronized (outputStream){
                        SocketUtil.write2Stream(protocol, outputStream);
                    }
                }
            }

            //循环结束退出输出流
            SocketUtil.closeOutputStream(outputStream);
        }
    }
}
